package timetabling;

import generalEvolutionaryAlgorithm.EABlackBoxFunction;
import generalEvolutionaryAlgorithm.EACrossover;
import generalEvolutionaryAlgorithm.EACrossoverClone;
import generalEvolutionaryAlgorithm.EAInstanceGenerator;
import generalEvolutionaryAlgorithm.EAMutation;
import generalEvolutionaryAlgorithm.EAResult;
import generalEvolutionaryAlgorithm.EASelection;
import generalEvolutionaryAlgorithm.EASelectionAllSurvive;
import generalEvolutionaryAlgorithm.EASelectionSurvivalOfTheFittestRandomTournament;
import generalEvolutionaryAlgorithm.EvolutionaryAlorithm;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;

public class TimetablingProblemEvolutionaryAlgorithm {
	public static void main(String[] args){
		File inputfile = null;
		if(args.length != 0){			
			inputfile = new File(args[args.length - 1]);
		}
		if(inputfile == null){
			System.err.println("No inputfile specified");
			System.exit(1);
		}
		BufferedReader reader = null;
		try {
			reader = new BufferedReader(new FileReader(inputfile));
		} catch (FileNotFoundException e) {
			try{
				System.err.println(new File("").getCanonicalPath());
			}catch(Exception ex){
				
			}
			e.printStackTrace();
			System.exit(1);
		}
		int events = 0, rooms = 0 , features = 0, students = 0;
		try{
			String inputNumbers[] = reader.readLine().split("[ ]+");
			events   = Integer.parseInt(inputNumbers[0]);
			rooms    = Integer.parseInt(inputNumbers[1]);
			features = Integer.parseInt(inputNumbers[2]);
			students = Integer.parseInt(inputNumbers[3]);
		} catch(Exception e){
			System.err.println("Could not read the first line of input file - it should contain 4 integers");
			e.printStackTrace();
			System.exit(1);
		}
		// room sizes
		int roomsize[] = new int[rooms];
		for(int i = 0; i < rooms; i++){
			try{
				roomsize[i] = Integer.parseInt(reader.readLine());
			} catch(Exception e){
				System.err.println("Could not read roomsizes");
				e.printStackTrace();
				System.exit(1);
			}
		}
		// student x event
		boolean studentAttendsEvent[][] = new boolean[students][events];
		for(int student = 0; student < students; ++student){
			try {
				for(int event = 0; event < events; ++event){
					studentAttendsEvent[student][event] = (Integer.parseInt(reader.readLine()) == 1);
				}
			} catch (Exception e) {
				System.err.println("Could not read which student attends which event");
				e.printStackTrace();
				System.exit(1);
			}
		}
		// room x feature
		int roomHasFeature[][] = new int[rooms][features];
		for(int room = 0; room < rooms; ++room){
			try {
				for(int feature = 0; feature < features; ++feature){
					roomHasFeature[room][feature] = Integer.parseInt(reader.readLine());
				}
			} catch (Exception e) {
				System.err.println("Could not read which student attends which event");
				e.printStackTrace();
				System.exit(1);
			}
		}
		// event x feature
		int eventNeedsFeature[][] = new int[events][features];
		for(int event = 0; event < events; ++event){
			try {
				for(int feature = 0; feature < features; ++feature){
					eventNeedsFeature[event][feature] = Integer.parseInt(reader.readLine());
				}
			} catch (Exception e) {
				System.err.println("Could not read which student attends which event");
				e.printStackTrace();
				System.exit(1);
			}
		}
		int eventSize[] = new int[events];
		for(int student = 0; student < students; ++student){
			for(int event = 0; event < events; ++event){
				if( studentAttendsEvent[student][event]){
					++eventSize[event];
				}
			}
		}
		boolean eventCanTakePartInRoom[][] = new boolean[events][rooms];
		for(int event = 0; event < events; ++event){
			for(int room = 0; room < rooms; ++room){
				boolean ok = roomsize[room] >= eventSize[event];
				for(int feature = 0; ok && feature < features; ++feature){
					if(eventNeedsFeature[event][feature] == 1){
						if(roomHasFeature[room][feature] == 0){
							ok = false;
						}
					}
				}
				eventCanTakePartInRoom[event][room] = ok;
			}
		}
		/* for debugging purposes:
		for(int student = 0; student < students; ++student){
			for(int event = 0; event < events; ++event){
				System.out.print((studentAttendsEvent[student][event]?1:0));
			}
			System.out.println();
		}
		System.out.println();
		for(int event = 0; event < events; ++event){
			for(int room = 0; room < rooms; ++room){
				System.out.print((eventCanTakePartInRoom[event][room]?1:0));
			}
			System.out.println();
		}
		System.out.println();
		System.out.println("SUCCESS");*/

		int populationSize = 20;
		int numberOfSteps = 1000;
		EABlackBoxFunction function = new TimetablingBlackBoxFunction(studentAttendsEvent, eventCanTakePartInRoom);
		// EAInstanceGenerator generator = new TimetablingGeneratorEmptyTimetable(studentAttendsEvent, eventCanTakePartInRoom);
		EAInstanceGenerator generator = new TimetablingGeneratorGreedy(studentAttendsEvent, eventCanTakePartInRoom);
		EASelection evolutionarySelection = new EASelectionAllSurvive();
		EACrossover crossover = new EACrossoverClone();
		EAMutation mutator = new TimetablingMutator2(studentAttendsEvent, eventCanTakePartInRoom);
		EASelection environmentalSelection = new EASelectionSurvivalOfTheFittestRandomTournament(populationSize);
		int nargs = args.length - 1;
		for(int i = 0; i < nargs; ++i){
			if(args[i].equals("-mutation1")){
				mutator = new TimetablingMutator1(studentAttendsEvent, eventCanTakePartInRoom);
			} else if(args[i].equals("-mutation2")){
				mutator = new TimetablingMutator2(studentAttendsEvent, eventCanTakePartInRoom);
			} else if(args[i].equals("-steps")){
				numberOfSteps = Integer.parseInt(args[++i]);
			} else if(args[i].equals("-population")){
				numberOfSteps = Integer.parseInt(args[++i]);
			} else if(args[i].equals("-generateFromFile")){
				generator = new TimetablingGeneratorReadFile1(studentAttendsEvent, eventCanTakePartInRoom, args[++i]);
			} else {
				System.out.println("command [" + args[i] +"] unknown");
			}
			// TODO check parameters
		}
		EAResult result = EvolutionaryAlorithm.doEvolutionaryAlgorithm(function, 
																		generator,
																		evolutionarySelection, 
																		crossover, 
																		mutator, 
																		environmentalSelection, 
																		populationSize, 
																		numberOfSteps, inputfile.getName() + mutator.getClass().getSimpleName() + "populationsize" + populationSize + "steps"+numberOfSteps);
		System.out.println("final result: " + result.optimalValue + " at position " + result.optimalInstance);
		System.exit(0);
	}
}
